package cn.com.cloudpioneer.evaluate.service;

import cn.com.cloudpioneer.evaluate.utils.JedisPoolUtil;
import cn.com.cloudpioneer.evaluate.utils.ObjectSerializeUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Service;
import redis.clients.jedis.Jedis;

import java.util.Collections;

/**
 * @auther maruikai
 * @create 2022/11/2-16:16
 */
@Service
public class RedisService implements InitializingBean {

    private static final Logger LOGGER = LoggerFactory.getLogger(RedisService.class);

    private static final String LOCK_SUCCESS = "OK";
    private static final String SET_IF_NOT_EXIST = "NX";
    private static final String SET_WITH_EXPIRE_TIME = "PX";

    private static final Long UNLOCK_SUCCESS = 1L;

    @Autowired
    private Environment environment;

    @Override
    public void afterPropertiesSet() throws Exception {

        JedisPoolUtil.initJedisPool(
                environment.getProperty("redis.host"),
                Integer.valueOf(environment.getProperty("redis.port")),
                environment.getProperty("redis.password"),
                Integer.valueOf(environment.getProperty("redis.database"))
        );
    }


    public Long expireKey(final String key, int seconds) {

        Jedis jedis = JedisPoolUtil.getJedis();

        try {
            return jedis.expire(key.getBytes(), seconds);
        } catch (Exception e) {
            LOGGER.error("expire key ERROR. key: {}", key);
            return -1L;
        } finally {
            JedisPoolUtil.closeJedis(jedis);
        }
    }

    //==========================  set operation key operation ==============================

    public String byteArraySet(final String key, byte[] bytes) {

        Jedis jedis = JedisPoolUtil.getJedis();

        try {
            return jedis.set(key.getBytes(), bytes);
        } catch (Exception e) {
            LOGGER.error("set ERROR. key: {}", key);
            return null;
        } finally {
            JedisPoolUtil.closeJedis(jedis);
        }
    }

    public byte[] byteArrayGet(final String key) {

        Jedis jedis = JedisPoolUtil.getJedis();

        try {
            return jedis.get(key.getBytes());
        } catch (Exception e) {
            LOGGER.error("get ERROR. key: {}", key);
            return null;
        } finally {
            JedisPoolUtil.closeJedis(jedis);
        }
    }

    public String objectSet(final String key, final Object object) {

        byte[] bytes = ObjectSerializeUtils.serializeObjectToBytes(object);

        if (bytes == null) {
            LOGGER.error("serialize object error. key: {}", key);
            return null;
        }

        return byteArraySet(key, bytes);
    }


    public Object objectGet(final String key) {

        byte[] bytes = byteArrayGet(key);

        if (bytes == null) return null;

        Object object = ObjectSerializeUtils.buildObjectFromBytes(bytes);

        if (object == null) {
            LOGGER.error("build object error. key: {}", key);
            return null;
        }

        return object;
    }


    public void lock(String key, int lockAliveTime) {
        try {
            boolean locked;
            while (true) {
                locked = tryLock(key, key, lockAliveTime);
                if (locked) {
                    return;
                }
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

    }


    public boolean tryLock(String key, int lockAliveTime) {
        return tryLock(key, key, lockAliveTime);
    }


    public boolean lock(String key, int waitTimeout, int lockAliveTime) {
        int w = waitTimeout * 1000;
        boolean locked;
        while (w > 0) {
            locked = tryLock(key, key, lockAliveTime);
            if (locked) {
                return true;
            }
            try {
                w -= 100;
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        return false;
    }


    public boolean unlock(String key) {
        return unlock(key, key);
    }

    /**
     * 尝试获取锁
     *
     * @param lockKey    锁 key
     * @param requestId  请求 id
     * @param expireTime 锁过期时间
     * @return
     */
    public boolean tryLock(String lockKey, String requestId, int expireTime) {
        Jedis jedis = JedisPoolUtil.getJedis();
        try {
            String result = jedis.set(lockKey, requestId, SET_IF_NOT_EXIST, SET_WITH_EXPIRE_TIME, expireTime);
            return LOCK_SUCCESS.equals(result);
        } finally {
            JedisPoolUtil.closeJedis(jedis);
        }

    }


    /**
     * @param lockKey
     * @param requestId
     * @return
     */
    public boolean unlock(String lockKey, String requestId) {
        Jedis jedis = JedisPoolUtil.getJedis();
        try {
            String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
            Object result = jedis.eval(script, Collections.singletonList(lockKey), Collections.singletonList(requestId));
            return UNLOCK_SUCCESS.equals(result);
        } finally {
            JedisPoolUtil.closeJedis(jedis);
        }
    }


}


